/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.archivers.tar;

import com.softwarecraftsmen.archivers.tar.fileEntries.FileEntry;
import com.softwarecraftsmen.archivers.tar.fileEntries.headers.FileHeaderWriter;
import com.softwarecraftsmen.archivers.tar.fileEntries.headers.FileHeaderFieldWriter;
import com.softwarecraftsmen.archivers.tar.fileEntries.headers.ByteWriter;
import static com.softwarecraftsmen.archivers.tar.BlockWriter.BlockSize;
import com.softwarecraftsmen.inputStreamReaders.InputStreamReader;
import com.softwarecraftsmen.inputStreamReaders.InputStreamReader.Nothing;
import org.jetbrains.annotations.NotNull;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;

public class TarCreator
{
	private final BlockWriter blockWriter;

	public TarCreator(final @NotNull OutputStream outputStream, final int numberOfBlocksPerRecord)
	{
		this.blockWriter = new BlockWriter(outputStream, numberOfBlocksPerRecord);
	}

	public void writeFileEntry(final @NotNull FileEntry fileEntry) throws IOException
	{
		writeHeader(fileEntry);
		writeFileContent(fileEntry);
		writeFooter();
	}

	public void finish() throws IOException
	{
		blockWriter.writeEndOfFileRecord();
	}

	private void writeHeader(final FileEntry fileEntry) throws IOException
	{
		final byte[] headerBlock = new byte[BlockSize];
		final FileHeaderWriter fileHeaderWriter = new FileHeaderWriter(new FileHeaderFieldWriter(new ByteWriter(headerBlock)));

		fileEntry.writeFileHeader(fileHeaderWriter);
		blockWriter.writeBlock(headerBlock);
	}

	private void writeFileContent(final FileEntry fileEntry) throws IOException
	{
		fileEntry.readData(new InputStreamReader<Nothing>()
		{
			@NotNull
			public Nothing read(final int inputBufferSize, final @NotNull BufferedInputStream inputStream) throws IOException
			{
				final byte[] block = new byte[BlockSize];
				int bytesRead;
				int totalBytesRead = 0;
				int offset = 0;
				int numberOfBytesToRead = BlockSize;
				while((bytesRead = inputStream.read(block, offset, numberOfBytesToRead)) != -1)
				{
					totalBytesRead = totalBytesRead + bytesRead;
					if (totalBytesRead == BlockSize)
					{
						blockWriter.writeBlock(block);
						totalBytesRead = 0;
						offset = 0;
						numberOfBytesToRead = BlockSize;
					}
					else
					{
						offset = totalBytesRead;
						numberOfBytesToRead = BlockSize - totalBytesRead;
					}
				}
				if (totalBytesRead != 0)
				{
					blockWriter.writeIncompleteBlock(block, totalBytesRead);
				}
				return Nothing;
			}
		});
	}

	private void writeFooter() throws IOException {blockWriter.writeEmptyBlocksUntilRecordFull();}
}